import 'dart:async';
import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

/// 离屏渲染
class OffScreenRender {
  static OffScreenRender? _singleton;

  OffScreenRender._inner();

  static OffScreenRender get _instance {
    return _singleton ??= OffScreenRender._inner();
  }

  factory OffScreenRender() => _instance;

  /// 将给定的Widget渲染成[ui.Image]。
  ///
  /// [width]和[height]表示成像图片的宽高，如果child未指定尺寸，此宽高也是child的最大宽高。
  ///
  /// [imageCompleter]表示给定的Widget是否包含[Image]组件。存在[Image]时则需要多次渲染才能得到最终图片数据。
  /// 因此需要传一个Completer表示最终图片是否加载完成。可通过[Image].frameBuilder中回调参数frame == 0来确定图片加载完成。
  ///
  /// [maxTryCount]表示最多尝试的渲染次数。如果在最大尝试次数还未得到内容，则渲染结果空白。
  ///
  /// [outputPixelRatio]为输出图片的像素比率，数值越搞，图片越高清。不传默认为当前设备的devicePixelRatio。
  /// 最终输出图片尺寸则为[width]和[height]乘以此值
  ///
  /// [renderDelay]表示每次渲染后等待时间，用于检测是否存在脏数据，需要重新渲染
  Future<ui.Image> render(Widget child, double width, double height,
      {Completer<bool>? imageCompleter,
      int maxTryCount = 10,
      double? outputPixelRatio,
      int renderDelay = 100}) async {
    var view = PlatformDispatcher.instance.views.single;
    var devicePixelRatio = view.devicePixelRatio;
    final RenderRepaintBoundary repaintBoundary = RenderRepaintBoundary();
    final RenderView renderView = RenderView(
      view: view,
      configuration: ViewConfiguration(
        size: Size(width, height),
        devicePixelRatio: devicePixelRatio,
      ),
      child: repaintBoundary,
    );

    PipelineOwner pipelineOwner = PipelineOwner();
    pipelineOwner.rootNode = renderView;
    renderView.prepareInitialFrame();

    bool isDirty = false;
    final BuildOwner buildOwner = BuildOwner(
      focusManager: FocusManager(),
      onBuildScheduled: () {
        // 表示widget有内容发生变更，需要重新绘制，此处基本就是图片引起的重绘
        isDirty = true;
      },
    );

    // 完成界面元素创建及布局
    final RenderObjectToWidgetElement<RenderBox> rootElement =
        RenderObjectToWidgetAdapter<RenderBox>(
      container: repaintBoundary,
      child: Directionality(
        textDirection: TextDirection.ltr,
        child: MediaQuery.fromView(
          view: view,
          child: child,
        ),
      ),
    ).attachToRenderTree(buildOwner);
    ui.Image image;
    int tryCount = 0;
    do {
      isDirty = false;
      image = await _rebuildChild(pipelineOwner, rootElement, buildOwner,
          repaintBoundary, outputPixelRatio ?? devicePixelRatio);
      await Future.delayed(Duration(milliseconds: renderDelay));
      tryCount++;
    } while (tryCount < maxTryCount &&
        (isDirty || imageCompleter?.isCompleted == false));
    return image;
  }

  /// 将[ui.Image]转成字节数组
  Future<Uint8List?> image2Bytes(ui.Image image,
      {ui.ImageByteFormat format = ui.ImageByteFormat.png}) async {
    final ByteData? byteData = await image.toByteData(format: format);
    var bytes = byteData?.buffer.asUint8List();
    return bytes;
  }

  Future<ui.Image> _rebuildChild(
      PipelineOwner pipelineOwner,
      Element child,
      BuildOwner buildOwner,
      RenderRepaintBoundary repaintBoundary,
      double outputPixelRatio) async {
    buildOwner.buildScope(child);
    buildOwner.finalizeTree();
    // 完成界面的绘制
    pipelineOwner.flushLayout();
    pipelineOwner.flushCompositingBits();
    pipelineOwner.flushPaint();
    return await repaintBoundary.toImage(pixelRatio: outputPixelRatio);
  }
}
